<script setup lang="ts">
import { nextTick, onErrorCaptured, ref } from 'vue';

const emit = defineEmits<{
  error: [error: Error];
  reset: [];
  retry: [];
}>();

const hasError = ref(false);
const errorMessage = ref('');
const errorDetails = ref('');
const isDev = ref(import.meta.env.DEV);

const handleError = (error: Error) => {
  console.error('ErrorBoundary 捕获错误:', error);

  hasError.value = true;
  errorMessage.value = error.message || '发生了未知错误';
  errorDetails.value = `${error.name}: ${error.message}\n${error.stack || ''}`;

  emit('error', error);
};

const retry = () => {
  hasError.value = false;
  errorMessage.value = '';
  errorDetails.value = '';

  nextTick(() => {
    emit('retry');
  });
};

const reset = () => {
  hasError.value = false;
  errorMessage.value = '';
  errorDetails.value = '';

  emit('reset');
};

// 捕获组件错误
onErrorCaptured((error: Error) => {
  handleError(error);
  return false; // 阻止错误继续传播
});

// 捕获未处理的 Promise 错误
if (typeof window !== 'undefined') {
  window.addEventListener('unhandledrejection', (event) => {
    console.error('未处理的 Promise 错误:', event.reason);
    if (event.reason instanceof Error) {
      handleError(event.reason);
    } else {
      handleError(new Error(String(event.reason)));
    }
    event.preventDefault();
  });
}
</script>

<template>
  <div v-if="hasError" class="error-boundary">
    <div class="error-content">
      <h3 class="mb-2 text-lg font-semibold text-red-600">出现错误</h3>
      <p class="mb-4 text-gray-700">{{ errorMessage }}</p>
      <div class="flex space-x-2">
        <button
          @click="retry"
          class="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600"
        >
          重试
        </button>
        <button
          @click="reset"
          class="rounded bg-gray-500 px-4 py-2 text-white hover:bg-gray-600"
        >
          重置
        </button>
      </div>
      <details v-if="isDev" class="mt-4">
        <summary class="cursor-pointer text-sm text-gray-500">
          错误详情 (开发模式)
        </summary>
        <pre class="mt-2 overflow-auto bg-gray-100 p-2 text-xs">{{
          errorDetails
        }}</pre>
      </details>
    </div>
  </div>
  <slot v-else></slot>
</template>

<style scoped>
.error-boundary {
  @apply rounded-lg border border-red-200 bg-red-50 p-6;
}

.error-content {
  @apply mx-auto max-w-md text-center;
}
</style>
